﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Observable;
using System.Collections.Specialized;

namespace ObservableUnitTests
{
    /// <summary>
    /// Summary description for ObservableListBaseTests
    /// </summary>
    [TestClass]
    public class ObservableListBaseTest
    {
        ObservableListBase<int, List<int>> ObservableList;
        List<NotifyCollectionChangedEventArgs> ObservableEvents;

        
        [TestInitialize]
        public void TestInitialize()
        {
            List<int> list = new List<int> { 0, 1, 2, 3, 4, 5 };

            ObservableList = new ObservableListBase<int, List<int>>(list);
            ObservableEvents = new List<NotifyCollectionChangedEventArgs>();

            ObservableList.CollectionChanged += (s, e) => { ObservableEvents.Add(e); };
        }

        
        [TestMethod]
        public void Insert_CheckEvent()
        {
            int value = 100;
            int index = 0;
            ObservableList.Insert(index, value);

            Assert.AreEqual(1, ObservableEvents.Count);
            
            NotifyCollectionChangedEventArgs args = ObservableEvents[0];

            Assert.AreEqual(args.Action, NotifyCollectionChangedAction.Add);
            
            Assert.IsNull(args.OldItems);
            Assert.IsNotNull(args.NewItems);
            Assert.AreEqual(1, args.NewItems.Count);

            Assert.AreEqual(index, args.NewStartingIndex);
            Assert.AreEqual(-1, args.OldStartingIndex);
            

            Assert.AreEqual(args.NewItems[0], value);
        }

        [TestMethod]
        public void Insert_CheckIndex()
        {
            int value = 10;
            for (int i = 0; i < 3; i++)
            {
                ObservableList.Insert(i, value+i);

                Assert.AreEqual(1, ObservableEvents.Count);
                NotifyCollectionChangedEventArgs args = ObservableEvents[0];

                Assert.AreEqual(i, args.NewStartingIndex);
                ObservableEvents.Clear();
            }
        }

        [TestMethod]
        public void RemoveAt_CheckEvent()
        {
            int index = 2;
            int expectedValue = index;
            ObservableList.RemoveAt(index);

            Assert.AreEqual(1, ObservableEvents.Count);

            NotifyCollectionChangedEventArgs args = ObservableEvents[0];

            Assert.AreEqual(args.Action, NotifyCollectionChangedAction.Remove);

            Assert.IsNull(args.NewItems);
            Assert.IsNotNull(args.OldItems);
            Assert.AreEqual(args.OldItems.Count, 1);

            Assert.AreEqual(-1, args.NewStartingIndex);
            Assert.AreEqual(index, args.OldStartingIndex);           
            
            Assert.AreEqual(args.OldItems[0], expectedValue);
        }

        [TestMethod]
        public void RemoveAt_CheckIndex()
        {
            for (int i = 0; i < ObservableList.Count; i++)
            {
                ObservableList.RemoveAt(i);

                Assert.AreEqual(1, ObservableEvents.Count);

                NotifyCollectionChangedEventArgs args = ObservableEvents[0];

                Assert.AreEqual(args.Action, NotifyCollectionChangedAction.Remove);
                Assert.AreEqual(i, args.OldStartingIndex);

                ObservableEvents.Clear();
            }
        }

        [TestMethod]
        public void SetByIndex_CheckEvent()
        {
            int index = 2;
            int oldValue = ObservableList[index];
            int newValue = 123;
            ObservableList[index] = newValue;

            Assert.AreEqual(1, ObservableEvents.Count);

            NotifyCollectionChangedEventArgs args = ObservableEvents[0];

            Assert.AreEqual(args.Action, NotifyCollectionChangedAction.Replace);

            Assert.IsNotNull(args.NewItems);
            Assert.IsNotNull(args.OldItems);
            
            Assert.AreEqual(args.NewItems.Count, 1);
            Assert.AreEqual(args.OldItems.Count, 1);

            Assert.AreEqual(index, args.NewStartingIndex);
            Assert.AreEqual(index, args.OldStartingIndex);

            Assert.AreEqual(args.NewItems[0], newValue);
            Assert.AreEqual(args.OldItems[0], oldValue);            
        }


        [TestMethod]
        public void SetByIndex_CheckIndex()
        {
            for (int i = 0; i < ObservableList.Count; i++)
            {
                ObservableList[i] =  ObservableList[i] + 1;

                Assert.AreEqual(1, ObservableEvents.Count);

                NotifyCollectionChangedEventArgs args = ObservableEvents[0];

                Assert.AreEqual(args.Action, NotifyCollectionChangedAction.Replace);
                Assert.AreEqual(i, args.NewStartingIndex);
                Assert.AreEqual(i, args.OldStartingIndex);

                ObservableEvents.Clear();
            }
        }

        [TestMethod]
        public void ListTransaction_MergeAdd()
        {
            int value = 100;
            int insertCount = 10;

            using (var transaction = ObservableList.BeginTransaction())
            {
                for (int i = 0; i < insertCount; i++)
                {
                    ObservableList.Insert(0, value);
                }

                //no events fired yet
                Assert.AreEqual(0, ObservableEvents.Count);
            }

            //verify
            Assert.AreEqual(1, ObservableEvents.Count);

            NotifyCollectionChangedEventArgs args = ObservableEvents[0];

            Assert.AreEqual(NotifyCollectionChangedAction.Add, args.Action);
            Assert.IsNotNull(args.NewItems);
            Assert.IsNull(args.OldItems);

            Assert.AreEqual(args.NewStartingIndex, 0);
            Assert.AreEqual(args.OldStartingIndex, -1);

            Assert.AreEqual(insertCount, args.NewItems.Count);
            foreach (int v in args.NewItems)
            {
                Assert.AreEqual(value, v);
            }
        }

        [TestMethod]
        public void ListTransaction_MergeRemove()
        {
            List<int> removedItems = new List<int>();

            using (var transaction = ObservableList.BeginTransaction())
            {
                while (ObservableList.Count > 0)
                {
                    removedItems.Add(ObservableList[0]);
                    ObservableList.RemoveAt(0);
                }

                //no events fired yet
                Assert.AreEqual(0, ObservableEvents.Count);
            }

            //verify
            Assert.AreEqual(1, ObservableEvents.Count);

            NotifyCollectionChangedEventArgs args = ObservableEvents[0];

            Assert.AreEqual(NotifyCollectionChangedAction.Remove, args.Action);
            Assert.IsNull(args.NewItems);
            Assert.IsNotNull(args.OldItems);

            Assert.AreEqual(-1, args.NewStartingIndex);
            Assert.AreEqual( 0, args.OldStartingIndex);

            Assert.AreEqual(removedItems.Count, args.OldItems.Count);

            for (int i = 0; i < removedItems.Count; i++)
            {
                Assert.AreEqual(removedItems[i], args.OldItems[i]);
            }
        }

        [TestMethod]
        public void ListTransaction_MultiMerge()
        {
            int addCount = 10;
            int removeCount = 10;
            int expectedEventCount = 0;

            using (var transaction = ObservableList.BeginTransaction())
            {
                //add 
                for (int i = 0; i < addCount; i++)
                {
                    ObservableList.Insert(0, i);
                }
                expectedEventCount++;

                //remove
                for (int i = 0; i < removeCount; i++)
                {
                    ObservableList.RemoveAt(0);
                }
                expectedEventCount++;

                //add 
                for (int i = 0; i < addCount; i++)
                {
                    ObservableList.Insert(0, i);
                }
                expectedEventCount++;

                //reset
                ObservableList.Clear();
                expectedEventCount += 1; //1 for remove all and 1 for reset event


                //no events fired yet
                Assert.AreEqual(0, ObservableEvents.Count);
            }

            //verify
            Assert.AreEqual(expectedEventCount, ObservableEvents.Count);
        }

    }
}
